/*
* Copyright 2011 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/
package org.etherframework.mediator
{
	import flash.display.DisplayObject;
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.utils.setTimeout;
	
	import mx.core.IVisualElement;
	import mx.events.FlexEvent;
	import mx.events.PropertyChangeEvent;
	
	import org.etherframework.common.IStatefulDestroyable;

	public final class Activator implements IStatefulDestroyable
	{
		private var _view:IVisualElement;
		private var _activatable:IActivatable;
		private var _active:Boolean = false;
		private var _initialized:Boolean = false;
		private var _pendingActivation:Boolean = false;
		private var _destroyed:Boolean = false;
		private var _enabled:Boolean = true;

		public function Activator(activatable:IActivatable, view:IVisualElement)
		{
			if (!activatable)
				throw new ArgumentError("Argument activatable:IActivatable can not be null.");
			if (!view)
				throw new ArgumentError("Argument view:DisplayObject can not be null.");
			
			_view = view;
			_activatable = activatable;
		}
		
		public function initialize():void
		{
			if (_initialized)
				throw new IllegalOperationError("Activator is already initialized.");
			
			const active:Boolean = isActive();
			if (_enabled)
			{
				addActivationListeners();
			}
			_initialized = true;
			
			if (active)
				activation(true);
		}
		
		public function destroy():void
		{
			removeActivationListeners();
			_view.removeEventListener(Event.ENTER_FRAME, viewEnterFrameHandler);
			_activatable = null;
			_view = null;
			_destroyed = true;
		}
		
		private function activation(value:Boolean):void
		{
			if (value == _active || !_enabled)
				return;
			
			_active = value;
			(_active) ? _activatable.activate() : _activatable.deactivate();
		}
		
		private function addActivationListeners():void
		{
			var item:IVisualElement = _view;
			const layerPropertyChangeEvent:String = "layerPropertyChange";
			
			item.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true);
			item.addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler, false, 0, true);
			
			while (item)
			{
				item.addEventListener(FlexEvent.HIDE, visibilityChangedHandler, false, 0, true);
				item.addEventListener(FlexEvent.SHOW, visibilityChangedHandler, false, 0, true);
				
				if (item.designLayer)
				{
					item.designLayer.addEventListener(layerPropertyChangeEvent, 
						designLayerPropertyChangeHandler, false, 0, true);
				}
				
				item = item.parent as IVisualElement;
			}
		}
		
		private function addedToStageHandler(event:Event):void
		{
			activation(isActive());
		}
		
		private function removedFromStageHandler(event:Event):void
		{
			activationChanged();
		}
		
		private function removeActivationListeners():void
		{
			var item:IVisualElement = _view;
			
			item.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
			item.removeEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
			
			while (item)
			{
				item.removeEventListener(FlexEvent.HIDE, visibilityChangedHandler);
				item.removeEventListener(FlexEvent.SHOW, visibilityChangedHandler);
				
				if (item.designLayer)
				{
					item.designLayer.removeEventListener("layerPropertyChange", 
						designLayerPropertyChangeHandler);
				}
				
				item = item.parent as IVisualElement;
			}
		}
		
		private function visibilityChangedHandler(event:FlexEvent):void
		{
			if (_destroyed)
				return;
			
			activationChanged();
		}
		
		private function designLayerPropertyChangeHandler(event:PropertyChangeEvent):void
		{
			if (_destroyed)
				return;
			
			if (event.property == "effectiveVisibility")
				activationChanged();
		}
		
		private function activationChanged():void
		{
			if (!_pendingActivation)
			{
				_view.addEventListener(Event.ENTER_FRAME, viewEnterFrameHandler);
				_pendingActivation = true;
			}
		}
		
		private function viewEnterFrameHandler(event:Event):void
		{
			event.target.removeEventListener(Event.ENTER_FRAME, viewEnterFrameHandler);
			
			if (_destroyed)
				return;
			
			if (_pendingActivation)
			{
				activation(isActive());
				_pendingActivation = false;
			}
		}
		
		private function isActive():Boolean
		{
			if (_view is DisplayObject && !_view["stage"])
				return false;
			
			var item:IVisualElement = _view;
			var isVisible:Boolean = true;
			
			while (item)
			{
				if (!item.visible || 
					(item.designLayer && !item.designLayer.effectiveVisibility))
				{isVisible = false; break;};
				item = item.parent as IVisualElement;
			}
			
			return isVisible;
		}
		
		public function get enabled():Boolean { return _enabled; }
		public function set enabled(value:Boolean):void
		{
			if (_enabled == value)
				return;
			
			_enabled = value;
			
			if (!_initialized)
				return;
			
			if (_enabled)
			{
				addActivationListeners();
			}
			else
			{
				removeActivationListeners();
			}
		}
		
		public function get active():Boolean { return _active; }
		
		public function get destroyed():Boolean { return _destroyed; }
		public function set destroyed(value:Boolean):void { _destroyed = value; }
	}
}